package com.meilai.project.service.finance.invoice.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.meilai.project.dto.finance.invoice.InvoiceApplyDTO;
import com.meilai.project.dto.workflow.FlowBusinessDataSuperEntity;
import com.meilai.project.dto.workflow.WorkFlowCheckDTO;
import com.meilai.project.entity.business.sale.BillSaleGroup;
import com.meilai.project.entity.business.sale.BillSaleGroupRelation;
import com.meilai.project.entity.finance.invoice.InvoiceApply;
import com.meilai.project.dto.finance.invoice.InvoiceApplyCheckDTO;
import com.meilai.project.dto.finance.invoice.InvoiceApplySearchDTO;
import com.meilai.project.entity.finance.invoice.InvoiceApplyAttachment;
import com.meilai.project.exception.CommonException;
import com.meilai.project.mapper.business.sale.BillSaleMapper;
import com.meilai.project.mapper.finance.invoice.InvoiceApplyMapper;
import com.meilai.project.service.business.sale.BillSaleGroupRelationService;
import com.meilai.project.service.business.sale.BillSaleGroupService;
import com.meilai.project.service.finance.invoice.InvoiceApplyAttachmentService;
import com.meilai.project.service.finance.invoice.InvoiceApplyService;
import com.meilai.project.service.system.impl.UserServiceImpl;
import com.meilai.project.util.ThreadLocalUserUtil;
import com.meilai.project.util.WorkFlowUtil;
import com.meilai.project.vo.business.sale.BillSaleGroupVO;
import com.meilai.project.vo.business.sale.BillSaleVO;
import com.meilai.project.vo.business.sale.ContractCustomerVO;
import com.meilai.project.vo.finance.invoice.InvoiceApplyVO;
import com.meilai.project.vo.system.UserForWFVO;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 开票申请表 服务实现类
 * </p>
 *
 * @author xhy
 * @since 2022-12-12
 */
@Service
public class InvoiceApplyServiceImpl extends ServiceImpl<InvoiceApplyMapper, InvoiceApply> implements InvoiceApplyService {

    @Autowired
    private BillSaleGroupService billSaleGroupService;

    @Autowired
    private BillSaleGroupRelationService billSaleGroupRelationService;

    @Autowired
    private BillSaleMapper billSaleMapper;

    @Autowired
    private InvoiceApplyAttachmentService invoiceApplyAttachmentService;


    @Override
    public Page<InvoiceApplyVO> selectPageList(InvoiceApplySearchDTO searchDTO) {
        if (!searchDTO.getManage()) {
            Long id = ThreadLocalUserUtil.get().getId();
            searchDTO.setUser_id(id);
        }
        Page<InvoiceApplyVO> page = searchDTO.createPage(InvoiceApplyVO.class);
        List<InvoiceApplyVO> listResult = baseMapper.selectPageList(page, searchDTO);
        if (CollectionUtils.isNotEmpty(listResult)) {
            Map<Long, ContractCustomerVO> map = billSaleGroupService.getContractAndCustomerByGroupIds(listResult.stream().map(InvoiceApplyVO::getGroup_id).collect(Collectors.toList()));
            for (InvoiceApplyVO invoiceApplyVO : listResult) {
                ContractCustomerVO vo = map.get(invoiceApplyVO.getGroup_id());
                if (vo != null) {
                    invoiceApplyVO.setContract_num(vo.getContract_num());
                    invoiceApplyVO.setContract_begin_date(vo.getContract_begin_date());
                    invoiceApplyVO.setContract_end_date(vo.getContract_end_date());
                    invoiceApplyVO.setProject_name(vo.getProject_name());
                }
            }
        }
        page.setRecords(listResult);
        return page;
    }

    @Override
    public InvoiceApplyVO getOneById(Long id) {
        InvoiceApplyVO result = baseMapper.getOneById(id);
        if (result == null) throw new CommonException("数据不存在！");
        if (null != id) {
            Long userId = ThreadLocalUserUtil.get().getId();
            boolean canCheck = WorkFlowUtil.canCheckData(userId, result);
            result.setCan_check(canCheck);
        }
        if (result.getGroup_id() == null) {
            throw new CommonException("数据异常，请联系管理员！");
        } else {
            ContractCustomerVO vo = billSaleGroupService.getContractAndCustomerByGroupId(result.getGroup_id());
            result.setContract_num(vo.getContract_num());
            result.setContract_begin_date(vo.getContract_begin_date());
            result.setContract_end_date(vo.getContract_end_date());
            result.setProject_name(vo.getProject_name());
            // 查出组关联的销售单信息
            List<BillSaleGroupRelation> relationList = billSaleGroupRelationService.list(Wrappers.<BillSaleGroupRelation>lambdaQuery().eq(BillSaleGroupRelation::getGroup_id, result.getGroup_id()));
            List<Long> bill_sale_ids = relationList.stream().map(BillSaleGroupRelation::getBill_sale_id).collect(Collectors.toList());
            // 查询销售单数据
            List<BillSaleVO> billSaleVOS = billSaleMapper.selectListOfIds(bill_sale_ids);
            result.setSaleVOList(billSaleVOS);
        }
        result.setApplyAttachments(invoiceApplyAttachmentService.list(Wrappers.<InvoiceApplyAttachment>lambdaQuery().eq(InvoiceApplyAttachment::getInvoice_apply_id, result.getId())));
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean save(InvoiceApplyDTO dto) {
        int flowResult = saveAndBegin(dto, true);
        if (flowResult != 1) throw new CommonException("保存至草稿箱失败，请联系管理员！");
        else return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean begin(InvoiceApplyDTO dto) {
        // 前置检查 此分组有通过或者流程中的开票申请 不允许提交
        int count = this.count(Wrappers.<InvoiceApply>lambdaQuery().isNull(InvoiceApply::getDeleted_at).and(i -> i.eq(InvoiceApply::getFlow_status, 3).or().eq(InvoiceApply::getFlow_status, 4)).eq(InvoiceApply::getGroup_id, dto.getGroup_id()));
        if (count > 0) {
            throw new CommonException("此分组已经有开票申请在流程中或通过审核 请勿重复提交");
        }
        // 提交
        int flowResult = saveAndBegin(dto, false);
        if (flowResult == 1) {
            // 仍在流程中
            return true;
        } else if (flowResult == 2) {
            // 无审批节点，直接结束
            return true;
        } else {
            throw new CommonException("提交失败，请联系管理员！");
        }
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean pass(InvoiceApplyCheckDTO dto) {
        // 通过
        int flowResult = passAndBack(dto, true, false);
        if (flowResult == 1) {
            // 通过后仍然在流程中
            return true;
        } else if (flowResult == 2) {
            // 审核后流程结束
            return true;
        } else {
            throw new CommonException("通过失败，请联系管理员！");
        }
    }

    private int saveAndBegin(InvoiceApplyDTO dto, boolean isSave) {
        // 获取当前人的id和部门id
        UserForWFVO user = UserServiceImpl.getUserForWF(dto);
        InvoiceApply temp = new InvoiceApply();
        BeanUtils.copyProperties(dto, temp);

        // 设置流程字段，flow_type请自行对应“dict_flow_type”表中的id， 比如当前入职流程（考察登记流程）的id是5
        String flow_title = temp.getCustomer_name() + "的开票申请"; // 审批流程的名称，需要根据实际业务自行确定
        FlowBusinessDataSuperEntity fbdse = WorkFlowUtil.getStartSuperDataEntity(
                user.getUser_id(),  // 当前用户id
                user.getDep_id(), // 提交的部门id
                23, // 流程类型id，“dict_flow_type”表中的id
                flow_title // 自定义的流程标题
        );
        BeanUtils.copyProperties(fbdse, temp);

        // 是否为新数据
        // 新数据时，前端不传业务数据id
        // 非新数据时，即来源是“草稿箱”、“已删除”、“被退回”时，前端需要传入业务数据id
        boolean isFirst = (dto.getId() == null);

        // 由于流程字段含有id，会覆盖数据，此处需要手动setId
        if (!isFirst) temp.setId(dto.getId());

        boolean flag = isFirst ? save(temp) : updateById(temp);

        // 构造流程工具类
        WorkFlowUtil wfu = new WorkFlowUtil<InvoiceApply, InvoiceApplyMapper>(temp.getId(), user.getUser_id(), InvoiceApply.class);

        // 审批流程id
        // 新数据,传0L
        // 非新数据,根据数据id,查找wfData_id
        Long wfData_id = isFirst ? 0L : wfu.getWfDataId(dto.getId());
        // 旧数据记录留存
        InvoiceApply old_data = isFirst ? new InvoiceApply() : temp;

        return isSave ? wfu.SaveDraft(wfData_id) : wfu.Begin(wfData_id, "", old_data);
    }

    private int passAndBack(InvoiceApplyCheckDTO dto, boolean isPass, boolean... updateData) {
        // 当前人id
        Long userId = ThreadLocalUserUtil.get().getId();

        // 若业务数据有审批中可修改的内容，请自行更新
        if (updateData != null && updateData[0]) {
            InvoiceApply updateTemp = new InvoiceApply();
            BeanUtils.copyProperties(dto, updateTemp);
            updateById(updateTemp);
        }

        // 业务数据
        InvoiceApply temp = getById(dto.getId());

        // 构造流程工具类
        WorkFlowUtil wfu = new WorkFlowUtil<InvoiceApply, InvoiceApplyMapper>(dto.getId(), userId, InvoiceApply.class);

        // 审批入参类
        WorkFlowCheckDTO checkDTO = WorkFlowCheckDTO.getWorkFlowCheckDTO(
                temp.getFlow_current_node_id(), // 当前节点id
                temp.getFlow_json(), // 流程json
                dto.getWf_check_opinion(), // 审批意见
                dto.getWf_check_sign_url(), // 审批签名链接
                JSONObject.toJSONString(temp) // 旧数据留存
        );

        // 审批
        return isPass ? wfu.ToNext(checkDTO) : wfu.ReturnBack(checkDTO);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean returnBack(InvoiceApplyCheckDTO dto) {
        // 驳回
        int flowResult = passAndBack(dto, false, false);
        if (flowResult != 1) throw new CommonException("驳回失败，请联系管理员！");
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean delete(Long id) {
        int flowResult = deleteAndActualDel(id, false);
        if (flowResult != 1) throw new CommonException("删除失败，请联系管理员！");
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean actualDelete(Long id) {
        int flowResult = deleteAndActualDel(id, true);
        if (flowResult != 1) throw new CommonException("彻底删除失败，请联系管理员！");
        return true;
    }

    // 删除 和 彻底删除 抽出相同代码
    private int deleteAndActualDel(Long id, boolean isActual) {
        // 当前人id
        Long userId = ThreadLocalUserUtil.get().getId();

        // 构造流程工具类
        WorkFlowUtil wfu = new WorkFlowUtil<InvoiceApply, InvoiceApplyMapper>(id, userId, InvoiceApply.class);

        // 流程id
        Long wfData_id = wfu.getWfDataId(id);

        return isActual ? wfu.ActualDelete(wfData_id) : wfu.Delete(wfData_id);
    }


    @Override
    public List<BillSaleGroupVO> getList(Long id) {
        // 获取组别的待选项 需要考虑到被退回的发票申请显示的问题
        Long user_id = ThreadLocalUserUtil.get().getId();
        List<InvoiceApply> list = this.list(Wrappers.<InvoiceApply>lambdaQuery().isNull(InvoiceApply::getDeleted_at).ne(id != null, InvoiceApply::getId, id));
        List<Long> group_ids = list.stream().map(InvoiceApply::getGroup_id).distinct().collect(Collectors.toList());
        List<BillSaleGroup> result = billSaleGroupService.list(Wrappers.<BillSaleGroup>lambdaQuery().notIn(CollectionUtils.isNotEmpty(group_ids), BillSaleGroup::getId, group_ids).eq(BillSaleGroup::getCreated_by, user_id));
        if (CollectionUtils.isEmpty(result)) {
            return new ArrayList<>();
        } else {
            List<Long> group_id_list = result.stream().map(BillSaleGroup::getId).collect(Collectors.toList());
            List<BillSaleGroupRelation> relationList = billSaleGroupRelationService.list(Wrappers.<BillSaleGroupRelation>lambdaQuery().in(BillSaleGroupRelation::getGroup_id, group_id_list));
            Map<Long, List<Long>> group_map = relationList.stream().collect(Collectors.groupingBy(BillSaleGroupRelation::getGroup_id, Collectors.mapping(BillSaleGroupRelation::getBill_sale_id, Collectors.toList())));
            List<Long> bill_sale_ids = relationList.stream().map(BillSaleGroupRelation::getBill_sale_id).collect(Collectors.toList());
            // 查询销售单数据
            List<BillSaleVO> billSaleVOS = billSaleMapper.selectListOfIds(bill_sale_ids);
            return result.stream().map(item -> {
                BillSaleGroupVO vo = new BillSaleGroupVO();
                BeanUtils.copyProperties(item, vo);
                List<Long> ids = group_map.get(vo.getId());
                if (CollectionUtils.isEmpty(ids)) {
                    vo.setSaleVOList(new ArrayList<>());
                } else {
                    List<BillSaleVO> collect = billSaleVOS.stream().filter(tmp -> ids.contains(tmp.getId())).collect(Collectors.toList());
                    vo.setSaleVOList(collect);
                }
                return vo;
            }).collect(Collectors.toList());
        }
    }
}
